﻿/*************************************************************************************
 * 文 件 名:   IHK_FileLoadUtility
 * 
 * 描    述:   加载图片、视频、音频的工具（应用了QF），该工具仅可用于PC端
 * 
 * 版    本：  V1.0
 * 创 建 者：  京产肠饭
 * 创建时间：  2022/5/13
 * 链   接：  https://gitee.com/JingChanChangFan/hk_-unity-tools
*************************************************************************************/

using System;
using System.Collections;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.Networking;
using QFramework;
using System.IO;
using UnityEditor;

public interface IHK_FileLoadUtility : IUtility
{
    /// <summary>
    /// 在磁盘中选择对应路径图片加载，并存储在StreamingAssets文件夹中
    /// 回调函数将返回：读取到的Texture2D 及 图片对应路径
    /// </summary>
    void LoadImage(Action<Texture2D, string> callback);

    /// <summary>
    /// 在磁盘中选择对应路径视频加载，并存储在StreamingAssets文件夹中
    /// 回调函数将返回：视频对应路径
    /// </summary>
    void LoadVideo(Action<string> callback);

    /// <summary>
    /// 在磁盘中选择对应路径音频加载，并存储在StreamingAssets文件夹中
    /// 回调函数将返回：音频对应路径
    /// </summary>
    void LoadAudio(Action<AudioClip, string> callback);
}

public class HK_FileLoadUtility : IHK_FileLoadUtility
{
    SourceLoader sourceLoader;

    /// <summary>
    /// 在磁盘中选择对应路径图片加载，并存储在StreamingAssets文件夹中
    /// 回调函数将返回：读取到的Texture2D 及 图片对应路径
    /// </summary>
    public void LoadImage(Action<Texture2D, string> callback)
    {
        OpenFileDlg fileDialog = new OpenFileDlg();
        fileDialog.structSize = Marshal.SizeOf(fileDialog);
        fileDialog.filter = "图片文件(*.jpg,*.png)\0*.jpg;*.png";
        fileDialog.file = new string(new char[256]);
        fileDialog.maxFile = fileDialog.file.Length;
        fileDialog.fileTitle = new string(new char[64]);
        fileDialog.maxFileTitle = fileDialog.fileTitle.Length;
        fileDialog.initialDir = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
        fileDialog.title = "选择文件";
        fileDialog.defExt = "png";//显示文件的类型
        fileDialog.flags = 0x00080000 | 0x00001000 | 0x00000800 | 0x00000200 | 0x00000008;
        fileDialog.dlgOwner = OpenFileDialog.GetForegroundWindow();

        if (OpenFileDialog.GetOpenFileName(fileDialog))
        {
            string filepath = fileDialog.file; //选择的文件路径; 
            string fileName = fileDialog.fileTitle;

            if (sourceLoader == null)
                sourceLoader = new GameObject().AddComponent<SourceLoader>();

            sourceLoader.LoadImg(fileName, filepath, callback);
        }
    }

    /// <summary>
    /// 在磁盘中选择对应路径视频加载，并存储在StreamingAssets文件夹中
    /// 回调函数将返回：视频对应路径
    /// </summary>
    public void LoadVideo(Action<string> callback)
    {
        OpenFileDlg fileDialog = new OpenFileDlg();
        fileDialog.structSize = Marshal.SizeOf(fileDialog);
        fileDialog.filter = "视频文件(*.mp4,*.mov)\0*.mp4;*.mov";
        fileDialog.file = new string(new char[256]);
        fileDialog.maxFile = fileDialog.file.Length;
        fileDialog.fileTitle = new string(new char[64]);
        fileDialog.maxFileTitle = fileDialog.fileTitle.Length;
        fileDialog.initialDir = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
        fileDialog.title = "选择文件";
        fileDialog.defExt = "mp4";//显示文件的类型
        fileDialog.flags = 0x00080000 | 0x00001000 | 0x00000800 | 0x00000200 | 0x00000008;
        fileDialog.dlgOwner = OpenFileDialog.GetForegroundWindow();

        if (OpenFileDialog.GetOpenFileName(fileDialog))
        {
            string filepath = fileDialog.file; //选择的文件路径; 
            string fileName = fileDialog.fileTitle;

            if (sourceLoader == null)
                sourceLoader = new GameObject().AddComponent<SourceLoader>();

            sourceLoader.LoadVideo(fileName, filepath, callback);
        }
    }

    /// <summary>
    /// 在磁盘中选择对应路径音频加载，并存储在StreamingAssets文件夹中
    /// 回调函数将返回：音频对应路径
    /// </summary>
    public void LoadAudio(Action<AudioClip, string> callback)
    {
        OpenFileDlg fileDialog = new OpenFileDlg();
        fileDialog.structSize = Marshal.SizeOf(fileDialog);
        fileDialog.filter = "音频文件(*.mp3,*.ogg)\0*.mp3;*.ogg";
        fileDialog.file = new string(new char[256]);
        fileDialog.maxFile = fileDialog.file.Length;
        fileDialog.fileTitle = new string(new char[64]);
        fileDialog.maxFileTitle = fileDialog.fileTitle.Length;
        fileDialog.initialDir = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
        fileDialog.title = "选择文件";
        fileDialog.defExt = "mp3";//显示文件的类型
        fileDialog.flags = 0x00080000 | 0x00001000 | 0x00000800 | 0x00000200 | 0x00000008;
        fileDialog.dlgOwner = OpenFileDialog.GetForegroundWindow();

        if (OpenFileDialog.GetOpenFileName(fileDialog))
        {
            string filepath = fileDialog.file; //选择的文件路径; 
            string fileName = fileDialog.fileTitle;

            if (sourceLoader == null)
                sourceLoader = new GameObject().AddComponent<SourceLoader>();

            sourceLoader.LoadAudio(fileName, filepath, callback);
        }
    }

    public class SourceLoader : MonoBehaviour
    {
        public void LoadImg(string imgName, string imgPath, Action<Texture2D, string> callback)
        {
            StartCoroutine(LoadImg_IE(imgName, imgPath, callback));
        }

        public void LoadVideo(string videoName, string videoPath, Action<string> callback)
        {
            StartCoroutine(LoadVideo_IE(videoName, videoPath, callback));
        }

        public void LoadAudio(string audioName, string audioPath, Action<AudioClip, string> callback)
        {
            StartCoroutine(LoadAudio_IE(audioName, audioPath, callback));
        }

        IEnumerator LoadImg_IE(string imgName, string imgPath, Action<Texture2D, string> callback)
        {
            if (!Directory.Exists(Application.streamingAssetsPath + "\\LoadImgs"))
                Directory.CreateDirectory(Application.streamingAssetsPath + "\\LoadImgs");

            string storePath = Application.streamingAssetsPath + "/LoadImgs/" + imgName;

            //创建一个负责读取的流
            using (FileStream fsRead = new FileStream(imgPath, FileMode.OpenOrCreate, FileAccess.Read))
            {
                //创建一个负责写入的流
                using (FileStream fdWrite = new FileStream(storePath, FileMode.OpenOrCreate, FileAccess.Write))
                {
                    // 5 兆的字节数组
                    byte[] buffer = new byte[1024 * 1024 * 5];
                    //因为文件可能会比较大，所以我们在读取的时候 应该通过一个循环去读取
                    while (true)
                    {
                        //返回本次读取实际读取到的字节数
                        int r = fsRead.Read(buffer, 0, buffer.Length);
                        //如果返回一个0  也就意味着什么都没有读取到 即读取完了
                        if (r == 0)
                            break;

                        fdWrite.Write(buffer, 0, r);
                    }
                }
            }

            yield return null;
#if UNITY_EDITOR
            AssetDatabase.Refresh();
#endif
            using (UnityWebRequest request = UnityWebRequestTexture.GetTexture(storePath))
            {
                yield return request.SendWebRequest();

                if (request.error != null)
                    Debug.Log(request.error);
                else if (request.isDone)
                {
                    Texture2D texture = DownloadHandlerTexture.GetContent(request);
                    callback?.Invoke(texture, storePath);
                }
            }
        }

        IEnumerator LoadVideo_IE(string videoName, string videoPath, Action<string> callback)
        {
            if (!Directory.Exists(Application.streamingAssetsPath + "\\LoadVideos"))
                Directory.CreateDirectory(Application.streamingAssetsPath + "\\LoadVideos");

            string storePath = Application.streamingAssetsPath + "/LoadVideos/" + videoName;

            //创建一个负责读取的流
            using (FileStream fsRead = new FileStream(videoPath, FileMode.OpenOrCreate, FileAccess.Read))
            {
                //创建一个负责写入的流
                using (FileStream fdWrite = new FileStream(storePath, FileMode.OpenOrCreate, FileAccess.Write))
                {
                    // 5 兆的字节数组
                    byte[] buffer = new byte[1024 * 1024 * 5];
                    //因为文件可能会比较大，所以我们在读取的时候 应该通过一个循环去读取
                    while (true)
                    {
                        //返回本次读取实际读取到的字节数
                        int r = fsRead.Read(buffer, 0, buffer.Length);
                        //如果返回一个0  也就意味着什么都没有读取到 即读取完了
                        if (r == 0)
                            break;

                        fdWrite.Write(buffer, 0, r);
                    }
                }
            }

            yield return null;
#if UNITY_EDITOR
            AssetDatabase.Refresh();
#endif
            callback(storePath);
        }

        IEnumerator LoadAudio_IE(string audioName, string audioPath, Action<AudioClip, string> callback)
        {
            if (!Directory.Exists(Application.streamingAssetsPath + "\\LoadAudios"))
                Directory.CreateDirectory(Application.streamingAssetsPath + "\\LoadAudios");

            string storePath = Application.streamingAssetsPath + "/LoadAudios/" + audioName;

            //创建一个负责读取的流
            using (FileStream fsRead = new FileStream(audioPath, FileMode.OpenOrCreate, FileAccess.Read))
            {
                //创建一个负责写入的流
                using (FileStream fdWrite = new FileStream(storePath, FileMode.OpenOrCreate, FileAccess.Write))
                {
                    // 5 兆的字节数组
                    byte[] buffer = new byte[1024 * 1024 * 5];
                    //因为文件可能会比较大，所以我们在读取的时候 应该通过一个循环去读取
                    while (true)
                    {
                        //返回本次读取实际读取到的字节数
                        int r = fsRead.Read(buffer, 0, buffer.Length);
                        //如果返回一个0  也就意味着什么都没有读取到 即读取完了
                        if (r == 0)
                            break;

                        fdWrite.Write(buffer, 0, r);
                    }
                }
            }

            yield return null;
#if UNITY_EDITOR
            AssetDatabase.Refresh();
#endif
            AudioClip audioClip = null;
            UnityWebRequest request;

            string audioExtention = audioName.Split('.')[1];
            if (audioExtention == "mp3")
                request = UnityWebRequestMultimedia.GetAudioClip(storePath, AudioType.MPEG);
            else
                request = UnityWebRequestMultimedia.GetAudioClip(storePath, AudioType.OGGVORBIS);

            yield return request.SendWebRequest();
            if (request.error != null)
                Debug.Log(request.error.ToString());
            else
                audioClip = DownloadHandlerAudioClip.GetContent(request);

            callback(audioClip, storePath);
        }
    }
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class OpenFileDlg
{
    public int structSize = 0;
    public IntPtr dlgOwner = IntPtr.Zero;
    public IntPtr instance = IntPtr.Zero;
    public String filter = null;
    public String customFilter = null;
    public int maxCustFilter = 0;
    public int filterIndex = 0;
    public String file = null;
    public int maxFile = 0;
    public String fileTitle = null;
    public int maxFileTitle = 0;
    public String initialDir = null;
    public String title = null;
    public int flags = 0;
    public short fileOffset = 0;
    public short fileExtension = 0;
    public String defExt = null;
    public IntPtr custData = IntPtr.Zero;
    public IntPtr hook = IntPtr.Zero;
    public String templateName = null;
    public IntPtr reservedPtr = IntPtr.Zero;
    public int reservedInt = 0;
    public int flagsEx = 0;
}

public class OpenFileDialog
{
    [DllImport("Comdlg32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)]
    public static extern bool GetOpenFileName([In, Out] OpenFileDlg ofd);
    [DllImport("Comdlg32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)]
    public static extern bool GetSaveFileName([In, Out] OpenFileDlg ofd);
    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    public static extern IntPtr GetForegroundWindow();
}

public static class Texture2DExt
{
    public static Sprite ToSprite(this Texture2D self)
    {
        var rect = new Rect(0, 0, self.width, self.height);
        var pivot = Vector2.one * 0.5f;
        var newSprite = Sprite.Create(self, rect, pivot);

        return newSprite;
    }
}
